package org.funsciencewithyourcomputer.client;

import java.util.List;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.DecoratedTabPanel;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.FocusListener;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.KeyboardListenerAdapter;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class Steg implements EntryPoint {

	Label label;

	ListBox modeBox;

	Grid grid;

	TextBox input;

	public static Button startButton;

	Dictionary dictionary = new Dictionary();

	Words words = new Words(dictionary);

	Lifecycle life = new Lifecycle();

	Score score = new Score(life, words);

	Grid rankingTableOriginal, rankingTableAction;

	int resumeTimeLeft = 0;

	Timer oneSecondTimer = new Timer() {
		public void run() {
			displayScore();
			if (!score.isGameOver())
				oneSecondTimer.schedule(1000);
		}
	};
	Timer newLineTimer = new Timer() {
		public void run() {
			boolean success = words.addLine(dictionary
					.createRandomLine(new int[] { 3, 4 }));
			if (!success) {
				gameOver();
				return;
			}
			score.increaseLevel();
			int speed = score.getSpeed();
			score.setTimeLevelEnd(System.currentTimeMillis() + speed);
			display();
			newLineTimer.schedule(speed);
		}
	};

	/**
	 * This is the entry point method.
	 */
	public void onModuleLoad() {
		DecoratedTabPanel tabPanel = new DecoratedTabPanel();
		tabPanel.setAnimationEnabled(true);
		DockPanel dockPanel = new DockPanel();
		dockPanel.setStyleName("mainPanel");
		dockPanel.setSpacing(10);
		dockPanel.setHorizontalAlignment(DockPanel.ALIGN_CENTER);
		HTML html = new HTML(
				"<a href=\"http://www.funsciencewithyourcomputer.org\">www.funsciencewithyourcomputer.org</a>",
				true);

		HTML htmlSwitch = new HTML(NLS.get(NLS.SWITCH_LANGUAGE), true);

		label = new Label();
		label.setText(NLS.get(NLS.GAME_NAME));

		modeBox = new ListBox();
		modeBox.addItem(NLS.get(NLS.MODE_ORIGINAL));
		modeBox.addItem(NLS.get(NLS.MODE_ACTION));
		modeBox.addChangeListener(new ChangeListener() {
			public void onChange(Widget sender) {
				updateMode(modeBox.getSelectedIndex() == 0 ? Lifecycle.MODE_ORIGINAL
						: Lifecycle.MODE_ACTION);
			}
		});

		VerticalPanel vPanel = new VerticalPanel();
		vPanel.setHorizontalAlignment(DockPanel.ALIGN_CENTER);

		grid = new Grid(Words.N_Lines, Words.N_Characters);
		grid.setStyleName(STYLE_gridGame);
		for (int row = 0; row < Words.N_Lines; row++) {
			for (int col = 0; col < Words.N_Characters; col++) {
				grid.getCellFormatter().setWidth(row, col, "14px");
				grid.getCellFormatter().setHeight(row, col, "14px");
				grid.getCellFormatter().setAlignment(row, col,
						HasHorizontalAlignment.ALIGN_CENTER,
						HasVerticalAlignment.ALIGN_MIDDLE);
			}
		}

		input = new TextBox();
		input.addKeyboardListener(new KeyboardListenerAdapter() {
			@Override
			public void onKeyUp(Widget sender, char keyCode, int modifiers) {
				Steg.this.onKeyUp(keyCode);
			}

		});
		input.addFocusListener(new FocusListener() {
			public void onLostFocus(Widget sender) {
			}

			public void onFocus(Widget sender) {
				if (input.getText().equals(NLS.get(NLS.INPUT_INFO))) {
					input.setText("");
					if (life.state == Lifecycle.START)
						started();
				}
			}
		});
		input.setText(NLS.get(NLS.INPUT_INFO));

		startButton = new Button(NLS.get(NLS.START));
		startButton.addClickListener(new ClickListener() {
			public void onClick(Widget sender) {
				if (life.state == Lifecycle.START)
					started();
				else if (life.state == Lifecycle.GAMEOVER)
					newGame();
				else if (life.state == Lifecycle.STARTED) {
					if (life.mode == Lifecycle.MODE_ORIGINAL)
						gameOver();
					else
						paused();
				}
				else if (life.state == Lifecycle.PAUSED)
					resume();
			}
		});

		rankingTableOriginal = new Grid(11, 3);
		rankingTableAction = new Grid(11,3);
		DecoratedTabPanel rankPanel = new DecoratedTabPanel();
		rankPanel.add(rankingTableOriginal, NLS.get(NLS.MODE_ORIGINAL));
		rankPanel.add(rankingTableAction, NLS.get(NLS.MODE_ACTION));
		rankPanel.selectTab(0);
		displayRanking();

		vPanel.add(grid);
		vPanel.add(input);

		dockPanel.add(modeBox, DockPanel.NORTH);
		dockPanel.add(label, DockPanel.NORTH);
		dockPanel.add(vPanel, DockPanel.CENTER);
		dockPanel.add(html, DockPanel.SOUTH);
		dockPanel.add(new HTML(NLS.get(NLS.COPYRIGHTS)), DockPanel.SOUTH);
		dockPanel.add(htmlSwitch, DockPanel.SOUTH);
		dockPanel.add(startButton, DockPanel.SOUTH);

		tabPanel.add(dockPanel, NLS.get(NLS.TAB_PLAY));
		tabPanel.add(rankPanel, NLS.get(NLS.TAB_RANKING));
		tabPanel.add(new HTML(NLS.get(NLS.TAB_HOWTO)), NLS.get(NLS.TAB_INFO));
		tabPanel.selectTab(0);
		RootPanel.get().add(tabPanel);

		life.state = Lifecycle.START;
	}

	public void started() {
		life.state = Lifecycle.LOADING;
		modeBox.setEnabled(false);
		startButton.setText(NLS.get(NLS.LOADING));
		dictionary.read(this);
		display();
	}

	public void paused() {
		life.state = Lifecycle.PAUSED;
		oneSecondTimer.cancel();
		newLineTimer.cancel();
		score.pause();
		resumeTimeLeft = score.getTimeLeft();
		startButton.setText(NLS.get(NLS.RESUME));
	}

	public void resume() {
		life.state = Lifecycle.STARTED;
		startButton.setText(NLS.get(NLS.PAUSE));
		if (life.mode == Lifecycle.MODE_ACTION) {
			oneSecondTimer.schedule(1000);
			score.setTimeLevelEnd(System.currentTimeMillis() + resumeTimeLeft
					* 1000);
			newLineTimer.schedule(resumeTimeLeft * 1000);
		}
	}

	public void dictionaryLoaded() {
		/**
		 * In original mode, fill up the grid.
		 * In action mode, fill up the first line.
		 */
		int limit = life.mode == Lifecycle.MODE_ORIGINAL ? Words.N_Lines : 1;
		for (int i = 0; i < limit; i++)
			words.addLine(dictionary.createRandomLine(new int[] { 3, 4 }));
		int speed = score.getSpeed();
		score.setTimeLevelEnd(System.currentTimeMillis() + speed);

		life.state = Lifecycle.STARTED;
		startButton.setText(NLS.get(life.mode == Lifecycle.MODE_ORIGINAL ? NLS.STOP_GAME : NLS.PAUSE));
		if (life.mode == Lifecycle.MODE_ACTION) {
			newLineTimer.schedule(speed);
			oneSecondTimer.schedule(1000);
		}
		display();
	}

	public void onKeyUp(char keyCode) {
		if (keyCode == '\r') {
			String letters = input.getText().toUpperCase();
			int count = words.removeAndFall(letters);
			if (count != 0)
				score.add(letters, count);
			input.setText("");
		}
		if (keyCode == ' ') {
			// add a new line
			boolean success = words.addLine(dictionary
					.createRandomLine(new int[] { 3, 4 }));
			if (!success) {
				gameOver();
				return;
			}
			score.add(null, Words.N_Characters);
			input.setText("");
		}
		display();
	}

	public void gameOver() {
		life.state = Lifecycle.GAMEOVER;
		modeBox.setEnabled(true);
		score.setGameOver();
		oneSecondTimer.cancel();
		newLineTimer.cancel();
		HighScore.saveCookie(score.getScore(), life.mode);
		displayRanking();
		display();
		startButton.setText(NLS.get(NLS.START));
	}

	public void newGame() {
		life = new Lifecycle();
		life.mode = modeBox.getSelectedIndex() == 0 ? Lifecycle.MODE_ORIGINAL : Lifecycle.MODE_ACTION;
		modeBox.setEnabled(false);
		words = new Words(dictionary);
		score = new Score(life, words);
		dictionaryLoaded();
	}

	public void updateMode(int newMode) {
		life.mode = newMode;
	}

	public void display() {
		displayLines();
		displayScore();
	}

	public void displayRanking() {
		rankingTableOriginal.setText(0, 0, NLS.get(NLS.RANKING_RANK));
		rankingTableOriginal.setText(0, 1, NLS.get(NLS.RANKING_NAME));
		rankingTableOriginal.setText(0, 2, NLS.get(NLS.RANKING_SCORE));
		HighScore ranking = HighScore.readCookie();
		for (int i = 0; i < HighScore.MAX_HIGH_SCORE; i++) {
			String name, score;
			if (ranking.originalName[i].equals(HighScore.NO_ENTRY)) {
				name = "---";
				score = name;				
			} else {
				name = ranking.originalName[i];
				score = ""+ranking.originalScore[i];
			}			
			rankingTableOriginal.setText(i + 1, 0, "" + (i + 1));
			rankingTableOriginal.setText(i + 1, 1, name);
			rankingTableOriginal.setText(i + 1, 2, score);
		}
		rankingTableAction.setText(0, 0, NLS.get(NLS.RANKING_RANK));
		rankingTableAction.setText(0, 1, NLS.get(NLS.RANKING_NAME));
		rankingTableAction.setText(0, 2, NLS.get(NLS.RANKING_SCORE));
		for (int i = 0; i < HighScore.MAX_HIGH_SCORE; i++) {
			String name, score;
			if (ranking.actionName[i].equals(HighScore.NO_ENTRY)) {
				name = "---";
				score = name;				
			} else {
				name = ranking.actionName[i];
				score = ""+ranking.actionScore[i];
			}			
			rankingTableAction.setText(i + 1, 0, "" + (i + 1));
			rankingTableAction.setText(i + 1, 1, name);
			rankingTableAction.setText(i + 1, 2, score);
		}
	}

	public void displayScore() {
		label.setText(score.getText());
	}

	public void displayLines() {
		String word = input.getText().toUpperCase();
		displayLines(words, word);
	}

	void displayLines(Words words, String word) {
		List<int[]> hIndeces = words.highlightIndeces(word, true);
		List<int[]> vIndeces = words.highlightIndeces(word, false);
		for (int row = 0; row < grid.getRowCount(); row++) {
			int index = grid.getRowCount() - 1 - row;
			String line = 0 <= index && index < words.lines.size() ? words.lines
					.get(index)
					: Words.EMPTY_LINE;
			for (int column = 0; column < grid.getColumnCount(); column++) {
				// grid.setText(row, column, "" + line.charAt(column));
				char character = line.charAt(column);
				if (character == ' ')
					character = '\u00A0';
				String text = "" + character;
				boolean hHot = words.isHot(index, column, hIndeces, true);
				boolean vHot = words.isHot(index, column, vIndeces, false);
				String style;
				if (hHot && vHot && word.length() > 1)
					style = STYLE_gridCellHotBoth;
				else if (hHot)
					style = STYLE_gridCellHotHorizontal;
				else if (vHot)
					style = STYLE_gridCellHotVertical;
				else
					style = STYLE_gridCellNormal;
				setText(grid, row, column, text, style);
			}
		}
	}

	public static final String STYLE_gridGame = "gridGame";

	public static final String STYLE_gridCellNormal = "gridCellNormal";

	public static final String STYLE_gridCellHotHorizontal = "gridCellHotHorizontal";

	public static final String STYLE_gridCellHotVertical = "gridCellHotVertical";

	public static final String STYLE_gridCellHotBoth = "gridCellHotBoth";

	static void setText(Grid grid, int row, int column, String text,
			String style) {
		String html = "<div class=\"" + style + "\">" + text.toUpperCase()
				+ "</div>";
		grid.setHTML(row, column, html);
	}
}
